//
// (C) Copyright 2009 Irantha Suwandarathna (irantha@gmail.com)
// All rights reserved.
//

/* Copyright (c) 2001-2008, The HSQL Development Group
 * All rights reserved.
 *
 * Redistribution and use _in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions _in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer _in the documentation
 * and/or other materials provided with the distribution.
 *
 * Neither the name of the HSQL Development Group nor the names of its
 * contributors may be used to endorse or promote products derived from this
 * software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


using System;
using System.IO;
using EffiProz.Core.Lib;
using EffiProz.Core.RowIO;


namespace EffiProz.Core.ScriptIO
{
    /**
     * Reader corresponding to BinaryDatabaseScritReader.
     *
     * @author fredt@users
     * @version 1.7.2
     * @since 1.7.2
     */
    class ScriptReaderBinary : ScriptReaderBase
    {

        private RowInputBinary rowIn;
        protected Stream dataStreamIn;

        public ScriptReaderBinary(Database db,
                           string file)
            : base(db, file)
        {


            rowIn = new RowInputBinary();
        }

        protected override void openFile()
        {

            dataStreamIn = db.getFileAccess().OpenInputStreamElement(fileName);
            
        }

        protected override void readDDL(Session session)
        {

            Result r = Result.read(rowIn, dataStreamIn);
            Iterator it = r.iterator();

            while (it.hasNext())
            {
                Object[] data = (Object[])it.next();
                string s = (String)data[0];
                Result result = session.sqlExecuteDirectNoPreChecks(s);

                if (result.isError())
                {
                    db.logger.appLog.logContext(SimpleLog.LOG_ERROR,
                                                result.getMainString());

                    /** @todo fredt - trap if unavaialble external functions are to be ignored */
                    throw Trace.error(result);
                }
            }
        }

        protected override void readExistingData(Session session)
        {

            for (; ; )
            {
                string s = readTableInit();

                if (s == null)
                {
                    break;
                }

                string schema = session.getSchemaName(currentSchema);
                Table t = db.schemaManager.getUserTable(session, s, schema);
                int j = 0;

                for (j = 0; ; j++)
                {
                    if (readRow(t) == false)
                    {
                        break;
                    }
                }

                int checkCount = readTableTerm();

                if (j != checkCount)
                {
                    throw Trace.error(Trace.ERROR_IN_SCRIPT_FILE,
                                      Trace.ERROR_IN_BINARY_SCRIPT_1,
                                      new Object[] {
                    s,   (j),   (checkCount)
                });
                }
            }
        }

        // int : row size (0 if no more rows) ,
        // BinaryServerRowInput : row (column values)
        protected  bool readRow(Table t)
        {

            bool more = readRow(rowIn, 0);

            if (!more)
            {
                return false;
            }

            Object[] data = rowIn.readData(t.getColumnTypes());

            t.insertFromScript(data);

            return true;
        }

        // int : rowcount
        protected int readTableTerm()
        {
            byte[] tmp = new byte[4];
            dataStreamIn.Read(tmp, 0, 4);
            return BitConverter.ToInt32(tmp, 0);
        }

        // int : headersize (0 if no more tables), string : tablename, int : operation,
        // string : schemaname
        protected string readTableInit()
        {

            bool more = readRow(rowIn, 0);

            if (!more)
            {
                return null;
            }

            string s = rowIn.readString();

            // operation is always INSERT
            int checkOp = rowIn.readIntData();

            if (checkOp == ScriptWriterBase.INSERT_WITH_SCHEMA)
            {
                currentSchema = rowIn.readString();
            }
            else
            {
                currentSchema = null;
            }

            if (checkOp != ScriptWriterBase.INSERT
                    && checkOp != ScriptWriterBase.INSERT_WITH_SCHEMA)
            {
                throw Trace.error(Trace.ERROR_IN_SCRIPT_FILE,
                                  Trace.ERROR_IN_BINARY_SCRIPT_2);
            }

            return s;
        }

        bool readRow(RowInputBase rowin, int pos)
        {

            try
            {

                byte[] tmp = new byte[4];
                dataStreamIn.Read(tmp, 0, 4);
       
                int length = BitConverter.ToInt32(tmp, 0);
                int count = 4;

                if (length == 0)
                {
                    return false;
                }

                rowin.resetRow(pos, length);

                dataStreamIn.Read(rowin.getBuffer(), count, length - count);

                return true;
            }
            catch (InvalidOperationException )
            {
                return false;
            }
        }

        public override bool readLoggedStatement(Session session)
        {
            return false;
        }

        public override void close()
        {

            try
            {
                dataStreamIn.Close();
            }
            catch (IOException) { }
        }
    }
}
